﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Security.AccessControl;
using System.Text;
using System.Threading;

namespace Sharp.Interprocess
{
    public class SharedHashset<T> : BaseSharedCollection<T>,ISet<T>
    {
        #region Members
        private readonly IEqualityComparer<T> m_ItemEqualityComparer;
        protected HashsetBucketCollection<T> m_HashsetBuckets;
        #endregion

        #region Constructor(s)

        public SharedHashset(string collectionMapName)
            : this(collectionMapName,EqualityComparer<T>.Default)
        {
        }

        public SharedHashset(string collectionMapName, IEqualityComparer<T> equalityComparer)
            : this(collectionMapName,equalityComparer,true,0)
        {
        }

        public SharedHashset(string collectionMapName, IEqualityComparer<T> equalityComparer, bool shouldAutoExpand, int collectionOffset = 0, int collectionMaxSize = PAGED_MEMORY_FILE_DEFAULT_SIZE)
            : base(collectionMapName,collectionOffset,shouldAutoExpand,collectionMaxSize)
        {
            m_ItemEqualityComparer = equalityComparer;
            m_HashsetBuckets = new HashsetBucketCollection<T>(collectionMapName);
        }

        #endregion

        #region ISet<T> Implementation

        public void AddRange(IEnumerable<T> items)
        {
            try
            {
                m_ReadWriteMutex.WaitOne();
                foreach (var item in items)
                {
                    StoreItem(item);
                }
            }
            finally
            {
                m_ReadWriteMutex.ReleaseMutex();
            }
        }

        public bool Add(T item)
        {
            try
            {
                m_ReadWriteMutex.WaitOne();
                StoreItem(item);
            }
            finally
            {
                m_ReadWriteMutex.ReleaseMutex();
            }
            return true;
        }

        public void ExceptWith(IEnumerable<T> other)
        {
            throw new NotImplementedException();
        }

        public void IntersectWith(IEnumerable<T> other)
        {
            throw new NotImplementedException();
        }

        public bool IsProperSubsetOf(IEnumerable<T> other)
        {
            throw new NotImplementedException();
        }

        public bool IsProperSupersetOf(IEnumerable<T> other)
        {
            throw new NotImplementedException();
        }

        public bool IsSubsetOf(IEnumerable<T> other)
        {
            throw new NotImplementedException();
        }

        public bool IsSupersetOf(IEnumerable<T> other)
        {
            throw new NotImplementedException();
        }

        public bool Overlaps(IEnumerable<T> other)
        {
            throw new NotImplementedException();
        }

        public bool SetEquals(IEnumerable<T> other)
        {
            var storedItemsHashSet = GetAllItems().ToHashSet();
            return storedItemsHashSet.SetEquals(other);            
        }

        public void SymmetricExceptWith(IEnumerable<T> other)
        {
            throw new NotImplementedException();
        }

        public void UnionWith(IEnumerable<T> other)
        {
            AddRange(other);
        }

        void ICollection<T>.Add(T item)
        {
            Add(item);
        }

        public void Clear()
        {
            ClearAllItems();
        }

        public bool Contains(T item)
        {
            try
            {
                m_ReadWriteMutex.WaitOne();
                var relevantItemOffsets = m_HashsetBuckets.GetItemOffsetsFromBucket(m_ItemEqualityComparer.GetHashCode(item));
                var relevantItems = GetItemsByOffsets(relevantItemOffsets);
                return relevantItems.Any(storedItem => m_ItemEqualityComparer.Equals(item, storedItem));
            }
            finally
            {
                m_ReadWriteMutex.ReleaseMutex();
            }
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            var storedItems = GetAllItems();
            storedItems.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get
            {
                return m_CollectionHeader.ItemCount; 
            }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(T item)
        {
            throw new NotImplementedException();
        }

        public IEnumerator<T> GetEnumerator()
        {
            var storedItems = GetAllItems();
            return storedItems.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return (System.Collections.IEnumerator)GetEnumerator();
        }

        #endregion

        #region IDisposable Implementation

        public override void Dispose()
        {
            base.Dispose();
            m_HashsetBuckets.Dispose();
        }

        #endregion

        #region Helper Methods

        private List<T> GetItemsByOffsets(IEnumerable<int> itemOffsets)
        {
            var storedItems = new List<T>();
            try
            {
                m_ReadWriteMutex.WaitOne();
                foreach (var itemOffset in itemOffsets)
                {
                    var mmObject = new MemoryMappedObject<T>(m_CollectionStorageFile, itemOffset);
                    storedItems.Add(mmObject.StoredObject);
                }
            }
            finally
            {
                m_ReadWriteMutex.ReleaseMutex();
            }

            return storedItems;
        }

        protected override List<T> GetAllItems()
        {
            var storedItemOffsets = m_HashsetBuckets.StoredHashBucketEntries.SelectMany(hashBucketInfo => hashBucketInfo.BucketMemberOffsets);
            return GetItemsByOffsets(storedItemOffsets);
        }

        protected override void StoreItem(T newItem, bool checkMemoryMappedFileCapacity = true)
        {
            if (!Contains(newItem))
            {
                
                var mmObject = new MemoryMappedObject<T>(newItem);
                if (checkMemoryMappedFileCapacity)
                {
                    EnlargeMemoryMappedFileSizeIfNeeded(mmObject.StoredSerializedObjectLength);
                }

                m_HashsetBuckets.AddItemToBucket(newItem, m_CollectionHeader.ItemLastOffset);
                mmObject.WriteToMemoryMappedFile(m_CollectionStorageFile, m_CollectionHeader.ItemLastOffset);
                m_CollectionHeader.UpdateOnItemAdded(mmObject.StoredSerializedObjectLength);
            }
        }

        protected override void ClearAllItems()
        {
            try
            {
                m_ReadWriteMutex.WaitOne();
                m_HashsetBuckets.ClearCollection();
                m_CollectionHeader.SetEmptyCollectionValues();
            }
            finally
            {
                m_ReadWriteMutex.ReleaseMutex();
            }
        }

        #endregion
    }
}
